import utils from 'utils';
import sha1 from 'sha1';
import remove from 'lodash.remove';
import queryString from 'query-string';
import CONSTANTS from 'utils/constants';
import * as actions from '../actions/index';

// Whether to show Merge Button, because there are most systems not need to show this button, so this place use === condition
function isNeededToDoMerge(result) {
  return result.is_support_merge;
}

function processCrDetail(result) {
  // Whether to show the merge button
  result.isNeededToDoMerge = isNeededToDoMerge(result);
  // Whether to show some modify buttons
  // If this value is undefined, then regard it as true.
  result.canModify = result.is_support_modify === undefined ? true : result.is_support_modify;
  return result;
}

// 页面初始化数据
function getInitialState(pageInfo) {
  const locationHash = window.location.hash;
  const hashStr = locationHash.split('#')[1] || '';
  const hashObject = queryString.parse(hashStr);
  const isShowCodeScan = !!utils.getUrlSearchParam('isShowCodeScan') || false;

  const { codeReviewDetail } = pageInfo;

  const initialState = {
    isShowComment: false,
    isShowCodeScan,
    isOnlyShowComments: true, // 是否仅显示评论
    commits: [],
    isLoadingCommits: false, // 正在请求commits区间
    to_commit: hashObject.to_commit || '',
    from_commit: hashObject.from_commit || '',
    hash: {}, // 自己实现路由
    codeReviewDetail,
    diffs: [], // 当前选中区间的diffs
    diffsSha1Map: {}, // diffs的new_path及其sha1的映射关系
    diffsConversationsMap: [], // 按diffs路径名归类的讨论
    pushRecords: [],
    isPushRecordsFetched: false,
    forceCrFileViewType: utils.getCachedData('forceCrFileViewType') || 'single', // 只展示一个文件或全部平铺
    forceDiffViewType: utils.getCachedData('forceDiffViewType') || 'split',
    forceIgnoreWhitespace: utils.getCachedData('forceIgnoreWhitespace') || 'false',
    isCommitsAndDiffsLoading: true,
    common_comments: [],
    inline_comments: [],
    allComments: [],
    conversations: [], // 行内评论形成的讨论，按parnet_note进行聚合
    drafts: [], // （行内）评论草稿
    writingChanges: [], // 标识正在编辑的行
    writingChangesEditors: {}, // 标识正在编辑的行对应的编辑器
    logs: [],
    summaryLogs: null,
    isShouldMergeChecked: false, // 合入目标分支的checkbox是否选中
    isShouldRemoveChecked: false, // 合并后删除原分支的checkbox是否选中
    from_push_id: null,
    to_push_id: null,
    range_context: '', // 评论上下文标识
    selectedCommitSha: null,
    lastCommit: {}, // 最新的一次 commit，用于测试结果展示
    testResult: null, // 测试结果
    p3cViolations: null, // 规约扫描结果
    p3cRuleDetail: null,
    diffsCount: null,
    workItems: null, // 关联工作项
    isTreeHide: false, // 是否隐藏改动左侧的树
    is_upvote: false, // 本人是否点赞
    upvoters: [], // 点赞列表
    labels: [], // 库下面所有的标签
    jump_line_code: null, // 待跳转的行号
    jump_file: hashObject.jump_file || null, // 待跳转的文件
    needIntro: !utils.getCachedData('forceCrIntroFinish'), // 是否需要引导
    diffTooLargeFlag: codeReviewDetail.diffTooLargeFlag, // diff是否过大
    commentsVisibleStatus: utils.getCachedData('commentsVisibleStatus') || 'all', // 评论展示与否的状态，可取3个值： all none unClosed
    changeRequest: null,
    inline_issues: [],
    active_line_codes: [], // 需要高亮的行号，某些场景，如hover到行内缺陷卡片，需要高亮一些行
    codeOwnerFocusMode: '', // CodeOwner专注模式，可选值为 own/all，缺省值为 all
    isShowExpiredPage: false, // 是否显示过期页面
    curBuildId: '', // 扫描面板中默认搜索项
    isLoadingMoreLine: false,
    // diff是否已请求到
    isDiffFetched: false,
    // 文件数量和改动行数
    totalCountInfo: {
      // 超过最大值，切换按钮禁用
      maxFileCount: CONSTANTS.ENUM.diffLimit.maxFileCount,
      maxLineCount: CONSTANTS.ENUM.diffLimit.maxLineCount,
      curFileCount: 0,
      curLineCount: 0,
      isTotalCountLoaded: false,
    },
    // 手动标记为未读的文件集合
    unreadFileObjectIds: [],
    // 用于保存各种版本的positon;
    // 类似：{[commit]: {path: []}}}
    positionMap: {},
    privateToken: '',
  };

  return initialState;
}

const defaultAction = {
  type: 'doNothing',
};

// 关闭 eslint 复杂度的校验
/* eslint complexity: "off" */
export default function index(state = getInitialState(window.FORCE), action = defaultAction) {
  switch (action.type) {
    case actions.UPDATE_DATA:
      return { ...state, ...action.data };
    case actions.CHANGE_ROUTER: {
      const hashString = action.data;
      const hash = queryString.parse(hashString);
      // 如果hash中带有commits id需要更新ID
      return {
        ...state,
        hash,
      };
    }
    case actions.RECEIVE_CRDETAIL: {
      const data = {
        ...state,
        codeReviewDetail: processCrDetail(action.data),
      };
      // codeOwnerFocusMode 默认为空，如果 code_owner_mode 为 FOCUS，则将其设为 own
      if (!state.codeOwnerFocusMode && action.data.code_owner_mode === 'FOCUS') {
        data.codeOwnerFocusMode = 'own';
      }
      return data;
    }
    case actions.LOADING_COMMITSANDDIFFS:
      return { ...state, isCommitsAndDiffsLoading: true };
    case actions.RECEIVE_DIFFS: {
      // TODO 这里有两种新、旧两种情况
      const diffs = action.data.changes || action.data;
      // 添加假的diff头部，此处 0299f74..23f1202 接口中没有，先写死了
      diffs.forEach((item) => {
        // 需用户手动点击加载时，不需这个头的情况
        if (item.diff !== '$WAITING_FOR_CLICK$' && item.diff !== '$WAITING_FOR_CLICK_BIG_SIZE$') {
          const fakeHeader = `diff --git a/${item.old_path} b/${item.new_path}\nindex 0299f74..23f1202 ${item.b_mode}\n`;
          item.diff = fakeHeader + item.diff;
        }
      });

      // 按diffs路径名归类的讨论
      const { conversations } = state;
      const diffsConversationsMap = {};
      diffs.forEach((diff) => {
        diffsConversationsMap[diff.new_path] = [];
        conversations.forEach((conversation) => {
          if (conversation[0].path === diff.new_path) {
            diffsConversationsMap[diff.new_path].push(conversation);
          }
        });
      });

      const diffsSha1Map = {};
      diffs.forEach((diff) => {
        diffsSha1Map[sha1(diff.new_path)] = diff.new_path;
      });

      return {
        ...state,
        diffs,
        diffsSha1Map,
        diffsConversationsMap,
        isCommitsAndDiffsLoading: false,
        isDiffFetched: true,
      };
    }
    case actions.RECEIVE_DIFFS_COUNT: {
      return {
        ...state,
        diffsCount: action.data,
      };
    }
    case actions.RECEIVE_SINGLE_DIFF: {
      const diffs = [...state.diffs];
      const singleDiff = action.data;
      // 添加假的diff头部，此处 0299f74..23f1202 接口中没有，先写死了
      diffs.forEach((item) => {
        // 需用户手动点击加载时，不需这个头的情况
        if (item.new_path === singleDiff.new_path && item.old_path === singleDiff.old_path) {
          // 如果单个diff大小大于 MAX_CR_DIFF_NUM，则不自动加载内容，让用户手动点击加载
          if (
            singleDiff.diff &&
            singleDiff.diff.length > CONSTANTS.MAX_CR_DIFF_SIZE &&
            item.diff !== '$WAITING_FOR_CLICK_BIG_SIZE$'
          ) {
            item.diff = '$WAITING_FOR_CLICK_BIG_SIZE$';
            delete item.isLoading;
          } else {
            const fakeHeader = `diff --git a/${item.old_path} b/${item.new_path}\nindex 0299f74..23f1202 ${item.b_mode}\n`;
            item.diff = fakeHeader + singleDiff.diff;
            item.showDiff = singleDiff.showDiff;
            item.is_new_lfs = singleDiff.is_new_lfs;
            item.is_old_lfs = singleDiff.is_old_lfs;
            delete item.isLoading;
          }
        }
      });

      return {
        ...state,
        diffs,
        isCommitsAndDiffsLoading: false,
      };
    }
    case actions.LODING_SINGLE_DIFF: {
      const diffs = [...state.diffs];
      const singleDiff = action.data;
      // 将该diff置为loading状态
      diffs.forEach((item) => {
        if (item.new_path === singleDiff.new_path && item.old_path === singleDiff.old_path) {
          item.isLoading = true;
        }
      });

      return {
        ...state,
        diffs,
        isCommitsAndDiffsLoading: false,
      };
    }
    case actions.RECEIVE_MORE_LINES: {
      // 在 hunkKey（即 @@ -6,10 +6,9 @@ 这种）后追加新获取的行，并替换成新计算的 hunkKey
      const { blob, hunk, diff, isAddToEnd, isLoadEndFinished } = action.data;
      const filepath = diff.newPath;
      const diffs = [...state.diffs];
      const blobArr = blob.replace(/\n$/, '').split('\n');
      const modifiedBlobArr = blobArr.map((item) => `${item}$BLOB_EXPANDED$`); // 每行结尾增加 $BLOB_EXPANDED$，作为展开行的标识
      const lineCnt = blobArr.length;
      const newBlob = modifiedBlobArr.join('\n ');
      const { newLines, newStart, oldLines, oldStart } = hunk;

      diffs.forEach((item) => {
        if (item.new_path === filepath) {
          const lines = item.diff.split('\n');
          const oldHunkKey = lines.find((line) => line.indexOf(hunk.content) === 0);
          if (hunk.content === 'STUB') {
            if (blobArr.length < 20) {
              item.loadEndFinished = true;
              if (newBlob && newBlob !== '$BLOB_EXPANDED$') {
                item.diff = `${item.diff}\n ${newBlob}`;
              }
            } else {
              item.diff = `${item.diff}\n ${newBlob}`;
              item.lastLineNum = item.lastLineNum ? item.lastLineNum + lineCnt : newStart + lineCnt;
              if (isLoadEndFinished) {
                item.loadEndFinished = true;
              }
            }
          } else if (!isAddToEnd) {
            // 向上展开，即在本hunk前面追加
            const newHunkKey = `@@ -${oldStart - lineCnt > 0 ? oldStart - lineCnt : 1},${
              oldLines + lineCnt
            } +${newStart - lineCnt > 0 ? newStart - lineCnt : 1},${newLines + lineCnt} @@`;
            const splitArr = item.diff.split(oldHunkKey);
            item.diff = `${splitArr[0]}${newHunkKey}\n ${newBlob}${splitArr[1]}`;
          } else {
            try {
              // 向下展开，即在本hunk的前一个hunk的最后追加
              const splitArr = item.diff.split(oldHunkKey);
              const prevOldHunkKeyIndex =
                (diff.hunks
                  ? diff.hunks.findIndex((item2) => oldHunkKey.indexOf(item2.content) === 0)
                  : -1) - 1;
              const prevOldHunk = diff.hunks[prevOldHunkKeyIndex];
              const prevOldHunkKey = prevOldHunk.content;
              const prevSplitArr = splitArr[0].split(prevOldHunkKey);
              const newHunkKey = `@@ -${prevOldHunk.oldStart},${prevOldHunk.oldLines + lineCnt} +${
                prevOldHunk.newStart
              },${prevOldHunk.newLines + lineCnt} @@`;
              item.diff = `${prevSplitArr[0]}${newHunkKey}${prevSplitArr[1]}\n ${newBlob}\n${oldHunkKey}${splitArr[1]}`;
            } catch (e) {
              console.error(e);
            }
          }
        }
      });

      return {
        ...state,
        diffs,
        isLoadingMoreLine: false,
      };
    }
    case actions.CHANGE_VIEWTYPE:
      return { ...state, forceDiffViewType: action.data };
    case actions.RECEIVE_COMMON_COMMENTS:
      return {
        ...state,
        common_comments: action.data.list,
      };
    case actions.RECEIVE_INLINE_COMMENTS: {
      const inline_comments = action.data.list;
      const { diffs } = state;

      // 递归形成讨论块：以无parent_note_id的评论作为讨论块的跟节点，其子评论平铺在下面
      const conversations = [];
      inline_comments.forEach((comment) => {
        const isRoot = !comment.parent_note_id;
        if (isRoot) {
          conversations.push([comment]);
        } else {
          conversations.forEach((conversation) => {
            conversation.forEach((childItem) => {
              if (childItem.id === comment.parent_note_id) {
                conversation.push(comment);
              }
            });
          });
        }
      });

      // 按diffs路径名归类的讨论
      const diffsConversationsMap = {};
      if (diffs) {
        diffs.forEach((diff) => {
          diffsConversationsMap[diff.new_path] = [];
          conversations.forEach((conversation) => {
            if (conversation[0].line_code.indexOf(sha1(diff.new_path)) !== -1) {
              diffsConversationsMap[diff.new_path].push(conversation);
            }
          });
        });
      }

      return {
        ...state,
        inline_comments,
        conversations,
        diffsConversationsMap,
      };
    }
    case actions.RECEIVE_LOGS:
      return {
        ...state,
        logs: action.data.list,
      };
    case actions.TOGGLE_MERGECHECKED:
      return {
        ...state,
        isShouldMergeChecked: !state.isShouldMergeChecked,
      };
    case actions.TOGGLE_DELETECHECKED:
      return {
        ...state,
        isShouldRemoveChecked: !state.isShouldRemoveChecked,
      };
    case actions.ADD_WRITINGCHANGES:
      return {
        ...state,
        writingChanges: [...state.writingChanges, action.data],
      };
    case actions.ADD_WRITINGCHANGESEDITOR: {
      const { lineId, editor } = action.data;
      state.writingChangesEditors[lineId] = editor;
      return {
        ...state,
        writingChangesEditors: state.writingChangesEditors,
      };
    }
    case actions.REMOVE_WRITINGCHANGESANDEDITOR: {
      const lineId = action.data;
      delete state.writingChangesEditors[lineId];
      remove(state.writingChanges, (item) => item === lineId);
      return {
        ...state,
        writingChanges: [...state.writingChanges],
        writingChangesEditors: state.writingChangesEditors,
      };
    }
    case actions.RECEIVE_PUSHRECORDS: {
      const pushRecords = action.data;

      let lastCommit;
      if (pushRecords.length) {
        // 从后向前遍历pushRecords.commits，取最近一个值
        for (let i = pushRecords.length - 1; i >= 0; i--) {
          const lastCommits = pushRecords[i].commits;
          if (lastCommits[lastCommits.length - 1]) {
            lastCommit = lastCommits[lastCommits.length - 1];
            break;
          }
        }
      } else {
        lastCommit = {};
      }

      return {
        ...state,
        pushRecords,
        isPushRecordsFetched: true,
        lastCommit,
      };
    }
    case actions.UPDATE_PUSH_ID:
      return {
        ...state,
        from_push_id: Number(action.data.from_push_id),
        to_push_id: Number(action.data.to_push_id),
        // range_context: `${action.data.from_push_id} ${action.data.to_push_id}`,
      };
    case actions.UPDATE_SELECTED_COMMIT_SHA:
      return {
        ...state,
        selectedCommitSha: action.data,
        // range_context: action.data,
      };
    case actions.RECEIVE_TEST_RESULT:
      return {
        ...state,
        testResult: action.data,
      };
    case actions.RECEIVE_P3C_VIOLATIONS:
      return {
        ...state,
        p3cViolations: action.data,
      };
    case actions.RECEIVE_INLINE_ISSUES:
      return {
        ...state,
        inline_issues: action.data,
      };
    case actions.RECEIVE_P3C_DETAIL:
      return {
        ...state,
        p3cRuleDetail: action.data,
      };
    case actions.RECEIVE_WORKITEM:
      return {
        ...state,
        workItems: action.data,
      };
    case actions.TOGGLE_TREE:
      return {
        ...state,
        isTreeHide: !state.isTreeHide,
      };
    case actions.UPDATE_TREE: {
      const originDiffs = [...state.diffs];
      const changesets = action.data;

      // 用新获取的 changesets 更新现有 diffs
      const diffs = originDiffs.map((diff, i) => ({ ...diff, ...changesets[i] }));

      return {
        ...state,
        diffs,
      };
    }
    case actions.RECEIVE_UPVOTE:
      return {
        ...state,
        is_upvote: action.data.is_upvote,
        upvoters: action.data.upvoters,
      };
    case actions.RECEIVE_LABELS:
      return {
        ...state,
        labels: action.data,
      };
    case actions.RECEIVE_JUMP_LINE_CODE:
      return {
        ...state,
        jump_line_code: action.data,
      };
    case actions.RECEIVE_JUMP_FILE:
      return {
        ...state,
        jump_file: action.data,
      };
    case actions.COMMENT_JUMP_CONNECT:
      return {
        ...state,
        comment_jump: action.data,
      };
    case actions.RECEIVE_CHANGE_REQUEST:
      return {
        ...state,
        codeReviewDetail: { ...state.codeReviewDetail, changeRequest: action.data },
      };
    case actions.FINISH_INTRO:
      utils.setCachedData('forceCrIntroFinish', 'true');
      return {
        ...state,
        needIntro: false,
      };
    case actions.CHANGE_COMMENTS_VISIBLE_STATUS:
      return {
        ...state,
        commentsVisibleStatus: action.data,
      };
    case actions.CHANGE_CODEOWNER_FOCUS_MODE:
      return {
        ...state,
        codeOwnerFocusMode: action.data,
      };
    case actions.OPEN_CODESCAN_PANEL_FROM_OVERVIEW:
      return {
        ...state,
        isShowCodeScan: action.data,
      };
    case actions.SET_BUILDID:
      return {
        ...state,
        curBuildId: action.data,
      };
    case actions.OPEN_COMMENT_PANEL_FROM_OVERVIEW:
      return {
        ...state,
        isShowComment: action.data,
      };
    case actions.SET_COMMENT_PANNEL_STATUS:
      return {
        ...state,
        curCommentStatus: action.data,
      };
    case actions.RESET_CHANGE_DATA:
      return {
        ...getInitialState(window.FORCE),
      };
    default:
      return state;
  }
}
